package conn

import (
	"net/url"
	"sync"
	"sync/atomic"

	"engine/log"
	"gitee.com/ameise84/e3lock"
	"gitee.com/ameise84/e3log"
	"gitee.com/ameise84/e3utils/safe"
	"gitee.com/ameise84/e3utils/str_conv"
	jsoniter "github.com/json-iterator/go"
	"google.golang.org/protobuf/proto"
	"servers/app/client/metas"
	"servers/internal/misc"
	"servers/internal/net_msg"
	"servers/models/define"
	"servers/models/error_code"
	"servers/models/meta"
)

type Client interface {
	e3log.LogMarshaller
	Send(reqMsg proto.Message, rspMsg proto.Message) error
	SetServerID(define.GameKind)
	GetAccountInfo() *metas.AccountInfo
	GetGameUrlParams() *meta.GameURLParams
	HttpClient
}

type client struct {
	GameClient
	HttpClient
	opt         *Option
	srvID       define.GameKind
	AcInfo      metas.AccountInfo
	UserID      define.UserIDType
	recvChan    chan net_msg.Message
	mu          e3lock.SpinLock
	params      *meta.GameURLParams
	waitRspName string
}

func (c *client) E3LogMarshall() string {
	return c.AcInfo.Account + ":" + str_conv.ToString(c.srvID)
}

func (c *client) GetGameUrlParams() *meta.GameURLParams {
	return c.params
}

func (c *client) Send(reqMsg proto.Message, rspMsg proto.Message) error {
	if err := c.GameClient.Send(c.srvID, reqMsg); err != nil {
		return err
	}
	c.mu.Lock()
	c.waitRspName = string(rspMsg.ProtoReflect().Descriptor().Name())
	msg := <-c.recvChan
	c.mu.Unlock()
	return proto.Unmarshal(msg.GetBody(), rspMsg)
}

func (c *client) SetServerID(srvID define.GameKind) {
	c.srvID = srvID
}

func (c *client) GetAccountInfo() *metas.AccountInfo {
	return &c.AcInfo
}

func (c *client) OnRecv(msg net_msg.Message) {

}

var userRunning atomic.Int64

func CheckUser() int64 {
	return userRunning.Load()
}

func Run(info *metas.AccountInfo, wg *sync.WaitGroup, opt *Option) {
	safe.Func(nil, "", func() {
		c := &client{
			opt:      opt,
			recvChan: make(chan net_msg.Message, 1),
			AcInfo:   *info,
		}

		c.HttpClient = NewHttpConn(opt.Addr)
		uid, code := createAccount(c)
		if code != error_code.ErrorCodeNoError {
			log.GetLogger().ErrorPrintf("create account failed:%v", code)
			return
		}
		c.params, code = getGameUrl(c)
		if code != error_code.ErrorCodeNoError {
			log.GetLogger().ErrorPrintf("get game url failed:%v", code)
			return
		}
		var err error
		c.GameClient, err = NewGameConn(c.params.GameUrl[0], c.OnRecv)
		if err == nil {
			userRunning.Add(1)
			c.UserID = uid
			opt.Task(c)
			userRunning.Add(-1)
		}
		wg.Done()
	})
}

func createAccount(c Client) (define.UserIDType, define.ErrorCode) {
	ac := c.GetAccountInfo()
	jsonRsp := metas.CreateRsp{}
	rsp, err := c.DoRequest(ReqGet, "/demo-api/create", map[string]string{
		define.WebAccount:    ac.Account,
		define.WebBusinessId: str_conv.ToString(ac.Bid),
	})
	if err != nil {
		return 0, error_code.ErrorCodeUnknown
	}
	_ = jsoniter.UnmarshalFromString(rsp, &jsonRsp)
	switch jsonRsp.Code {
	case error_code.ErrorCodeNoError, error_code.ErrorCodeAccountIsExist:
		return jsonRsp.Data.Uid, error_code.ErrorCodeNoError
	default:
		return 0, jsonRsp.Code
	}
}

func getGameUrl(c Client) (*meta.GameURLParams, define.ErrorCode) {
	ac := c.GetAccountInfo()
	jsonRsp := metas.GameUrlRsp{}
	rsp, err := c.DoRequest(ReqGet, "/demo-api/get-game-url", map[string]string{
		define.WebBusinessId: str_conv.ToString(ac.Bid),
		define.WebAccount:    ac.Account,
		define.WebGuest:      "false",
		define.WebLang:       "cn",
		define.WebGameKind:   str_conv.ToString(ac.GameKindID),
	})
	if err != nil {
		return nil, error_code.ErrorCodeUnknown
	}
	_ = jsoniter.UnmarshalFromString(rsp, &jsonRsp)
	switch jsonRsp.Code {
	case error_code.ErrorCodeNoError:
		uri, err := url.Parse(jsonRsp.Data)
		if err != nil {
			return nil, error_code.ErrorCodeProtocolFailed
		}
		queryParams := uri.Query()
		urlToken := queryParams.Get("token")
		if len(urlToken) == 0 {
			log.GetLogger().Warn("token len =0").Println()
			return nil, error_code.ErrorCodeProtocolFailed
		}
		urlParams := meta.GameURLParams{}
		if err = misc.UnparseGameUrlToken(_gDefaultAes, urlToken, &urlParams); err != nil {
			return nil, error_code.ErrorCodeProtocolFailed
		}
		return &urlParams, error_code.ErrorCodeNoError
	default:
		return nil, jsonRsp.Code
	}
}
